import os
import sys
import time
import base64
import socket
import hashlib
import logging
import logging.handlers
import threading
import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler

from pydaemon.daemon import Daemon

EXTHOST = ""
EXTPORT = 9999
LOCALPORT = 9998

class AuthXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
    def authenticate_client(self):
        auth = False
        if self.headers.has_key("Authorization"):
            enctype, encstr = self.headers.get('Authorization').split()
            uid, pwd = base64.standard_b64decode(encstr).split(':')
            if self.server.usermap.get(uid) == pwd:
                auth = True
        return auth

    def _dispatch(self, method, params):
        auth = self.authenticate_client()
        if auth:
            return self.server._dispatch(method, params)
        else:
            raise Exception("Not authenticated! %s" % self.server.usermap)

class SuperDaemon(Daemon):
    
    dostop = False
    usermap = {'foo':'bar'}

    def __init__(self, name, extfuncs, *kwds, **args):
        self.name = name
        self.extfuncs = extfuncs
        self.logfile = "/var/log/%s.log" % name
        self.log = logging.getLogger(name)
        self.log.setLevel(logging.DEBUG)
        loghandler =  logging.handlers.RotatingFileHandler(
            self.logfile,
            maxBytes = 1048576,
            backupCount = 5
        )
        formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
        loghandler.setFormatter(formatter)
        self.log.addHandler(loghandler)
        Daemon.__init__(self, *kwds, **args)

    def localXMLRPC(self, port):
        """localXMLRPC - XMLRPC server for locally available functions."""

        self.log.debug("localXMLRPC")
        try:
            server = SimpleXMLRPCServer(("localhost", port))
            self.log.info("XMLRPC Server listening on (%s, %s)" %
                    ("localhost", port)
            )
            server.register_introspection_functions()
            server.register_function(self.safeStop)
            server.serve_forever()
        except Exception, err:
            self.log.exception(err)

    def serveXMLRPC(self, host, port):
        """serveXMLRPC - XMLRPC server for publically available functions."""

        #FIXME This probably needs some kind of security
        self.log.debug("serveXMLRPC")
        try:
            server = SimpleXMLRPCServer((host, port), 
                    requestHandler=AuthXMLRPCRequestHandler)
            server.usermap = self.usermap
            self.log.info("XMLRPC Server listening on (%s, %s)" % 
                    (host, port)
            )
            server.register_introspection_functions()
            for func in self.extfuncs:
                if hasattr(self, func):
                    self.log.info("Registering function: %s" % func)
                    server.register_function(getattr(self, func))
                else:
                    self.log.error("Function %s not available" % func)
            server.serve_forever()
        except Exception, err:
            self.log.exception(err)

    def run(self):
        """run - Kicked off from daemon.start.  This is our worker process."""
        self.log.info("Run Daemon.")

        # Kickoff a thread for exposing some items via XMLRPC
        self.log.debug("Starting XMLRPC Listeners")
        try:
            t1 = threading.Thread(target=self.serveXMLRPC, args=(EXTHOST, EXTPORT))
            t2 = threading.Thread(target=self.localXMLRPC, args=(LOCALPORT,))
            self.log.info("Threads created")
            t1.start()
            t2.start()
        except Exception, err:
            self.log.error("Trouble setting up XMLRPC Thread!")
            self.log.exception(err)
        self.log.info("Thread started")

        ret = 0
        while not ret:
            try:
                if self.dostop:
                    self.log.info("Stopping...")
                    self.shuttingDown = False
                else:
                    self.work()
            except Exception, err:
                self.log.error("ERROR DETECTED")
                self.log.exception(err)
                self.log.info("Continue testing")
            time.sleep(10)

        self.log.info("Join XMLRPC Server thread.")
        t1.join(10)
        t2.join(10)

    def safeStop(self):
        """safeStop - Attempt to safely schedule a stop."""
        self.dostop = True
        self.log.info("Scheduling shutdown...")
        self.shuttingDown = True
        while self.shuttingDown:
            time.sleep(2)
        return 0

    def work(self):
        """ Implement Me!!"""
        pass

PWDFILE = "~/.testpwdfile"

class ExampleFuncs():
    def sayHello(self):
        return "Hello World!!"

class ExampleServer(SuperDaemon, ExampleFuncs):
    def __init__(self, *kwds, **args):
        SuperDaemon.__init__(self, 'exserver', ['sayHello'], *kwds, **args)
        self.setupAuth()
        self.log.info("Initializing ExampleServer Daemon")

    def setupAuth(self):
        try:
            self.log.info("Opening file %s" % os.path.abspath(PWDFILE))
            h = open(PWDFILE)
            try:
                self.usermap = eval(h.read())
            finally:
                h.close()
        except IOError:
            self.log.info("Could not open %s" % PWDFILE)
            self.usermap = {'admin':'password'}

    def _run(self):
        self.log.info("RUN!")
        time.sleep(5) 

if __name__ == "__main__":
    daemon = ExampleServer('/tmp/ex-server.pid')
    if len(sys.argv) == 2:
        if 'start' == sys.argv[1]:
            daemon.start()
        elif 'stop' == sys.argv[1]:
            try:
                xmlrpclib.ServerProxy("http://%s:%s" % ("localhost", LOCALPORT)).safeStop()
            except socket.error:
                print "Appears to already be stopped."
            daemon.stop()
        elif 'restart' == sys.argv[1]:
            xmlrpclib.ServerProxy("http://%s:%s" % ("localhost", LOCALPORT)).safeStop()
            daemon.restart()
        elif 'forcestop' == args[0]:
            daemon.stop()
        else:
            print "Unknown command"
            sys.exit(2)
        sys.exit(0)
    else:
        print "usage: %s start|stop|restart" % sys.argv[0]
        sys.exit(2)
